/////////////////////////////////////////////////////////////////////////////
//
// (c) 2007 BinaryComponents Ltd.  All Rights Reserved.
//
// http://www.binarycomponents.com/
//
/////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace SuperListEx.Utility.Collections
{
  /// <summary>
  /// List style class that fires events when items are added / deleted.
  /// </summary>
  public class EventingList<T> : IList<T>
  {
    #region vars
    private List<T> _items = new List<T>();
    private bool _allowsDup = true;
    #endregion

    #region events
    public event EventHandler<EventInfo<T>> PreDataChanged;
    public event EventHandler<EventInfo<T>> DataChanged;
    #endregion

    #region properties
    public T[] ToArray()
    {
      return _items.ToArray();
    }

    protected List<T> UnderlyingList
    {
      get
      {
        return _items;
      }
    }

    public int Count
    {
      get
      {
        return _items.Count;
      }
    }

    public bool IsReadOnly
    {
      get
      {
        return false;
      }
    }

    public T this[int index]
    {
      get
      {
        return _items[index];
      }
      set
      {
        if (!_items[index].Equals(value))
        {
          T itemToRemove = _items[index];

          OnPreDataChanged(new EventInfo<T>(EventType.Deleted, itemToRemove));
          OnPreDataChanged(new EventInfo<T>(EventType.Added, value));

          _items[index] = value;

          OnDataChanged(new EventInfo<T>(EventType.Deleted, itemToRemove));
          OnDataChanged(new EventInfo<T>(EventType.Added, value));
        }
      }
    }

    public bool AllowsDuplicates
    {
      get
      {
        return _allowsDup;
      }
      set
      {
        _allowsDup = value;
      }
    }
    #endregion

    #region Event handlers
    protected virtual void OnPreDataChanged(EventInfo<T> eventInfo)
    {
      if (PreDataChanged != null)
      {
        PreDataChanged(this, eventInfo);
      }
    }

    protected virtual void OnDataChanged(EventInfo<T> eventInfo)
    {
      if (DataChanged != null)
      {
        DataChanged(this, eventInfo);
      }
    }
    #endregion

    #region IList<T> Members

    public int IndexOf(T item)
    {
      return _items.IndexOf(item);
    }


    public void Insert(int index, T item)
    {
      if (!AllowsDuplicates && _items.Contains(item)) return;
      OnPreDataChanged(new EventInfo<T>(EventType.Added, item));
      _items.Insert(index, item);
      OnDataChanged(new EventInfo<T>(EventType.Added, item));
    }

    public void RemoveAt(int index)
    {
      OnPreDataChanged(new EventInfo<T>(EventType.Deleted, _items[index]));
      _items.RemoveAt(index);
      OnDataChanged(new EventInfo<T>(EventType.Deleted, _items[index]));
    }

    public void MoveItem(int from, int to)
    {
      if (from < 0 || from >= _items.Count)
      {
        throw new ArgumentOutOfRangeException("from");
      }
      if (to < 0 || to >= _items.Count)
      {
        throw new ArgumentOutOfRangeException("to");
      }

      T temp = _items[from];
      _items.RemoveAt(from);
      _items.Insert(to, temp);
    }



    #endregion

    #region ICollection<T> Members

    public void Add(T item)
    {
      if (!AllowsDuplicates && _items.Contains(item)) return;
      OnPreDataChanged(new EventInfo<T>(EventType.Added, item));
      _items.Add(item);
      OnDataChanged(new EventInfo<T>(EventType.Added, item));
    }

    public void AddRange(IEnumerable<T> items)
    {
      List<T> copy = new List<T>(items);
      if (!AllowsDuplicates)
      {
        copy = (from i in copy select i).Distinct().ToList();
      }

      OnPreDataChanged(new EventInfo<T>(EventType.Added, items));
      _items.AddRange(copy);
      OnDataChanged(new EventInfo<T>(EventType.Added, items));
    }

    public virtual void Clear()
    {
      if (_items.Count > 0)
      {
        OnPreDataChanged(new EventInfo<T>(EventType.Deleted, _items));
        _items.Clear();
        OnDataChanged(new EventInfo<T>(EventType.Deleted, _items));
      }
    }

    public bool Contains(T item)
    {
      return _items.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
      _items.CopyTo(array, arrayIndex);
    }

    public void RemoveRange(int start, int count)
    {
      if (_items.Count == 0) return;
      List<T> items = _items.GetRange(start, count);
      OnPreDataChanged(new EventInfo<T>(EventType.Deleted, items));
      _items.RemoveRange(start, count);
      OnDataChanged(new EventInfo<T>(EventType.Deleted, items));
    }

    public void RemoveRange(IEnumerable<T> items)
    {
      OnPreDataChanged(new EventInfo<T>(EventType.Deleted, items));
      foreach (T t in items) Remove(t);
      OnDataChanged(new EventInfo<T>(EventType.Deleted, items));
    }

    public bool Remove(T item)
    {
      OnPreDataChanged(new EventInfo<T>(EventType.Deleted, item));
      _items.Remove(item);
      OnDataChanged(new EventInfo<T>(EventType.Deleted, item));
      return false;
    }

    #endregion

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    {
      return _items.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
      return _items.GetEnumerator();
    }

    #endregion
  }
}